/*
 * DIM-SUM操作系统 -- 异常处理
 *
 * Copyright (C) 2023 国科础石(重庆)软件有限公司
 *
 * 作者: Dong Peng <w-pengdong@kernelsoft.com>
 *
 * License terms: GNU General Public License (GPL) version 3
 *
 */

#include <dim-sum/linkage.h>
#include <uapi/asm-generic/unistd.h>
#include <asm/asm.h>
#include <asm/csr.h>
#include <asm/asm-offsets.h>

	.text
	.altmacro

	/**
 	 * 将所有寄存器保存到堆栈，为进入系统调用或异常做准备
	 */
	.macro SAVE_ALL
	LOCAL _restore_kernel_tpsp
	LOCAL _save_context

	/**
	 * 如果来自用户空间，则保留用户进程指针并加载内核进程指针
	 * 如果来自内核，sscratch将包含0，我们应该继续使用当前的TP
	 */
	csrrw tp, sscratch, tp
	bnez tp, _save_context

_restore_kernel_tpsp:
	csrr tp, sscratch
	STORE_REG sp, TASK_TI_KERNEL_SP(tp)
_save_context:
	STORE_REG sp, TASK_TI_USER_SP(tp)
	LOAD_REG sp, TASK_TI_KERNEL_SP(tp)
	addi sp, sp, -(ESPOT_SIZE_ON_STACK)
	/**
	 * 保存当前struct exception_spot*
	 */
	STORE_REG sp, TASK_TI_REGS_SP(tp)
	
	STORE_REG x1,  ESPOT_RA(sp)
	STORE_REG x3,  ESPOT_GP(sp)
	STORE_REG x5,  ESPOT_T0(sp)
	STORE_REG x6,  ESPOT_T1(sp)
	STORE_REG x7,  ESPOT_T2(sp)
	STORE_REG x8,  ESPOT_S0(sp)
	STORE_REG x9,  ESPOT_S1(sp)
	STORE_REG x10, ESPOT_A0(sp)
	STORE_REG x11, ESPOT_A1(sp)
	STORE_REG x12, ESPOT_A2(sp)
	STORE_REG x13, ESPOT_A3(sp)
	STORE_REG x14, ESPOT_A4(sp)
	STORE_REG x15, ESPOT_A5(sp)
	STORE_REG x16, ESPOT_A6(sp)
	STORE_REG x17, ESPOT_A7(sp)
	STORE_REG x18, ESPOT_S2(sp)
	STORE_REG x19, ESPOT_S3(sp)
	STORE_REG x20, ESPOT_S4(sp)
	STORE_REG x21, ESPOT_S5(sp)
	STORE_REG x22, ESPOT_S6(sp)
	STORE_REG x23, ESPOT_S7(sp)
	STORE_REG x24, ESPOT_S8(sp)
	STORE_REG x25, ESPOT_S9(sp)
	STORE_REG x26, ESPOT_S10(sp)
	STORE_REG x27, ESPOT_S11(sp)
	STORE_REG x28, ESPOT_T3(sp)
	STORE_REG x29, ESPOT_T4(sp)
	STORE_REG x30, ESPOT_T5(sp)
	STORE_REG x31, ESPOT_T6(sp)

	/*
	 * 禁用用户模式内存访问，因为它只能在实际的用户复制例程中设置
	 * 禁用FPU，以检测内核空间中浮点运算的非法使用
	 */
	li t0, SR_SUM | SR_FS

	LOAD_REG s0, TASK_TI_USER_SP(tp)
	csrrc s1, sstatus, t0
	csrr s2, sepc
	csrr s3, sbadaddr
	csrr s4, scause
	csrr s5, sscratch
	STORE_REG s0, ESPOT_SP(sp)
	STORE_REG s1, ESPOT_SSTATUS(sp)
	STORE_REG s2, ESPOT_SEPC(sp)
	STORE_REG s3, ESPOT_SBADADDR(sp)
	STORE_REG s4, ESPOT_SCAUSE(sp)
	STORE_REG s5, ESPOT_TP(sp)
	.endm

	/**
	 * 准备从系统调用或异常中返回，从堆栈中恢复所有寄存器
	 */
	.macro RESTORE_ALL
	LOAD_REG a0, ESPOT_SSTATUS(sp)
	LOAD_REG a2, ESPOT_SEPC(sp)
	csrw sstatus, a0
	csrw sepc, a2

	LOAD_REG x1,  ESPOT_RA(sp)
	LOAD_REG x3,  ESPOT_GP(sp)
	LOAD_REG x4,  ESPOT_TP(sp)
	LOAD_REG x5,  ESPOT_T0(sp)
	LOAD_REG x6,  ESPOT_T1(sp)
	LOAD_REG x7,  ESPOT_T2(sp)
	LOAD_REG x8,  ESPOT_S0(sp)
	LOAD_REG x9,  ESPOT_S1(sp)
	LOAD_REG x10, ESPOT_A0(sp)
	LOAD_REG x11, ESPOT_A1(sp)
	LOAD_REG x12, ESPOT_A2(sp)
	LOAD_REG x13, ESPOT_A3(sp)
	LOAD_REG x14, ESPOT_A4(sp)
	LOAD_REG x15, ESPOT_A5(sp)
	LOAD_REG x16, ESPOT_A6(sp)
	LOAD_REG x17, ESPOT_A7(sp)
	LOAD_REG x18, ESPOT_S2(sp)
	LOAD_REG x19, ESPOT_S3(sp)
	LOAD_REG x20, ESPOT_S4(sp)
	LOAD_REG x21, ESPOT_S5(sp)
	LOAD_REG x22, ESPOT_S6(sp)
	LOAD_REG x23, ESPOT_S7(sp)
	LOAD_REG x24, ESPOT_S8(sp)
	LOAD_REG x25, ESPOT_S9(sp)
	LOAD_REG x26, ESPOT_S10(sp)
	LOAD_REG x27, ESPOT_S11(sp)
	LOAD_REG x28, ESPOT_T3(sp)
	LOAD_REG x29, ESPOT_T4(sp)
	LOAD_REG x30, ESPOT_T5(sp)
	LOAD_REG x31, ESPOT_T6(sp)

	LOAD_REG x2,  ESPOT_SP(sp)
	.endm


ENTRY(handle_exception)
	SAVE_ALL

	/**
	 * 将sscratch寄存器设置为0，
	 * 这样如果出现递归异常，异常向量就会知道它来自内核
	 */
	csrw sscratch, x0

	/* 加载全局指针 */
.option push
.option norelax
	la gp, __global_pointer$
.option pop

	la ra, exception_return
	/* 根据scase的MSB可区分中断和异常 */
	bge s4, zero, exception_case

irq_case:
	/* 处理中断 */
	move a0, sp /* exception_spot */
	tail handle_irq_fault

exception_case:
	/* 开启中断处理异常 */
	csrs sstatus, SR_SIE

	/* 处理系统调用 */
	li t0, EXC_SYSCALL
	beq s4, t0, handle_syscall

	/* 处理其它异常 */
	slli t0, s4, RISCV_LGPTR
	la t1, excp_vect_table
	la t2, excp_vect_table_end
	move a0, sp /* exception_spot */
	add t0, t1, t0
	/* 检查异常码 */
	bgeu t0, t2, unknown_fault
	LOAD_REG t0, 0(t0)
	jalr t0

	j exception_return
	/* 异常后期处理完成 */

unknown_fault:
	tail handle_unknown_fault

handle_syscall:
	/* 保存a0的初始值（信号处理程序需要） */
	STORE_REG a0, ESPOT_ORIG_A0(sp)
	/**
	 * 提前保存SEPC 以避免在sret上执行原始scall指令
	 */
	addi s2, s2, 0x4
	STORE_REG s2, ESPOT_SEPC(sp)

	/* 检查系统调用号 */
	li t0, __NR_syscalls
	la s0, sys_call_unknown
	/* a7寄存器保存系统调用号 */
	bgeu a7, t0, err_or_sys_call
	la s0, syscall_table
	slli t0, a7, RISCV_LGPTR
	add s0, s0, t0
	LOAD_REG s0, 0(s0)
	/* 调用系统调用 */
err_or_sys_call:
	jalr s0

syscall_return:
	/* 设置系统调用返回值 */
	STORE_REG a0, ESPOT_A0(sp)
	
exception_return:
	/* 调用异常后期处理 */
	/* 关闭中断 */
	csrc sstatus, SR_SIE 
	move a0, sp
	call exception_tail
	/* 开启中断 */
	csrs sstatus, SR_SIE
	/* 异常后期处理完成 */

	LOAD_REG s0, ESPOT_SSTATUS(sp)
	csrc sstatus, SR_SIE
	andi s0, s0, SR_SPP
	bnez s0, restore_all

	/* 保存进程内核栈到PROCESS_DESC中 */
	addi s0, sp, ESPOT_SIZE_ON_STACK
	STORE_REG s0, TASK_TI_KERNEL_SP(tp)

	/**
	 * 保存TP到sscratch寄存器中
	 */
	csrw sscratch, tp

restore_all:
	RESTORE_ALL
	sret

END(handle_exception)


ENTRY(start_user_run)
	/* 关闭中断 */
	csrc sstatus, SR_SIE 
	move sp, a0
	call init_user_context

	/* 保存进程内核栈到PROCESS_DESC中 */
	addi s0, sp, ESPOT_SIZE_ON_STACK
	STORE_REG s0, TASK_TI_KERNEL_SP(tp)
	/* 保存TP到sscratch, 下一次查找进程内核数据结构 */
	csrw sscratch, tp

	RESTORE_ALL
	sret
ENDPROC(start_user_run)

ENTRY(ret_from_fork)
	move a0, sp
	call fork_ret_before

	/* 关闭中断 */
	csrc sstatus, SR_SIE 	
	/* 保存进程内核栈到PROCESS_DESC中 */
	addi s0, sp, ESPOT_SIZE_ON_STACK
	STORE_REG s0, TASK_TI_KERNEL_SP(tp)
	/* 保存TP到sscratch, 下一次查找进程内核数据结构 */
	csrw sscratch, tp
	RESTORE_ALL
	sret
ENDPROC(ret_from_fork)

/*
 * 整数寄存器上下文切换
 * 必须保存和恢复已保存的寄存器。
 *
 * a0: 上一个进程结构体指针
 * a1: 下一个进程结构体指针
 * a2: 下一个进程的栈指针
 *
 */
ENTRY(__switch_cpu_asm)
	/* 保存上一个进程的上下文 */
	li    a3,  TASK_SPOT_RA
	add   a0, a0, a3
	add   a1, a1, a3
	STORE_REG ra,  TASK_SPOT_RA_RA(a0)
	STORE_REG sp,  TASK_SPOT_SP_RA(a0)
	STORE_REG s0,  TASK_SPOT_S0_RA(a0)
	STORE_REG s1,  TASK_SPOT_S1_RA(a0)
	STORE_REG s2,  TASK_SPOT_S2_RA(a0)
	STORE_REG s3,  TASK_SPOT_S3_RA(a0)
	STORE_REG s4,  TASK_SPOT_S4_RA(a0)
	STORE_REG s5,  TASK_SPOT_S5_RA(a0)
	STORE_REG s6,  TASK_SPOT_S6_RA(a0)
	STORE_REG s7,  TASK_SPOT_S7_RA(a0)
	STORE_REG s8,  TASK_SPOT_S8_RA(a0)
	STORE_REG s9,  TASK_SPOT_S9_RA(a0)
	STORE_REG s10, TASK_SPOT_S10_RA(a0)
	STORE_REG s11, TASK_SPOT_S11_RA(a0)
	/* 加载下一个进程的上下文 */
	LOAD_REG ra,  TASK_SPOT_RA_RA(a1)
	LOAD_REG sp,  TASK_SPOT_SP_RA(a1)
	LOAD_REG s0,  TASK_SPOT_S0_RA(a1)
	LOAD_REG s1,  TASK_SPOT_S1_RA(a1)
	LOAD_REG s2,  TASK_SPOT_S2_RA(a1)
	LOAD_REG s3,  TASK_SPOT_S3_RA(a1)
	LOAD_REG s4,  TASK_SPOT_S4_RA(a1)
	LOAD_REG s5,  TASK_SPOT_S5_RA(a1)
	LOAD_REG s6,  TASK_SPOT_S6_RA(a1)
	LOAD_REG s7,  TASK_SPOT_S7_RA(a1)
	LOAD_REG s8,  TASK_SPOT_S8_RA(a1)
	LOAD_REG s9,  TASK_SPOT_S9_RA(a1)
	LOAD_REG s10, TASK_SPOT_S10_RA(a1)
	LOAD_REG s11, TASK_SPOT_S11_RA(a1)
	move tp, a2
	ret
ENDPROC(__switch_cpu_asm)

/*
 * 保存浮点寄存器上下文
 *
 * a0: 进程结构体指针
 *
 */
ENTRY(__fstate_save_asm)
	li  a2,  TASK_SPOT_F0
	add a0, a0, a2
	li t1, SR_FS
	csrs CSR_STATUS, t1
	frcsr t0
	fsd f0,  TASK_SPOT_F0_F0(a0)
	fsd f1,  TASK_SPOT_F1_F0(a0)
	fsd f2,  TASK_SPOT_F2_F0(a0)
	fsd f3,  TASK_SPOT_F3_F0(a0)
	fsd f4,  TASK_SPOT_F4_F0(a0)
	fsd f5,  TASK_SPOT_F5_F0(a0)
	fsd f6,  TASK_SPOT_F6_F0(a0)
	fsd f7,  TASK_SPOT_F7_F0(a0)
	fsd f8,  TASK_SPOT_F8_F0(a0)
	fsd f9,  TASK_SPOT_F9_F0(a0)
	fsd f10, TASK_SPOT_F10_F0(a0)
	fsd f11, TASK_SPOT_F11_F0(a0)
	fsd f12, TASK_SPOT_F12_F0(a0)
	fsd f13, TASK_SPOT_F13_F0(a0)
	fsd f14, TASK_SPOT_F14_F0(a0)
	fsd f15, TASK_SPOT_F15_F0(a0)
	fsd f16, TASK_SPOT_F16_F0(a0)
	fsd f17, TASK_SPOT_F17_F0(a0)
	fsd f18, TASK_SPOT_F18_F0(a0)
	fsd f19, TASK_SPOT_F19_F0(a0)
	fsd f20, TASK_SPOT_F20_F0(a0)
	fsd f21, TASK_SPOT_F21_F0(a0)
	fsd f22, TASK_SPOT_F22_F0(a0)
	fsd f23, TASK_SPOT_F23_F0(a0)
	fsd f24, TASK_SPOT_F24_F0(a0)
	fsd f25, TASK_SPOT_F25_F0(a0)
	fsd f26, TASK_SPOT_F26_F0(a0)
	fsd f27, TASK_SPOT_F27_F0(a0)
	fsd f28, TASK_SPOT_F28_F0(a0)
	fsd f29, TASK_SPOT_F29_F0(a0)
	fsd f30, TASK_SPOT_F30_F0(a0)
	fsd f31, TASK_SPOT_F31_F0(a0)
	sw t0, TASK_SPOT_FCSR_F0(a0)
	csrc CSR_STATUS, t1
	ret
ENDPROC(__fstate_save_asm)

/*
 * 加载浮点寄存器上下文
 *
 * a0: 进程结构体指针
 *
 */
ENTRY(__fstate_restore_asm)
	li  a2,  TASK_SPOT_F0
	add a0, a0, a2
	li t1, SR_FS
	lw t0, TASK_SPOT_FCSR_F0(a0)
	csrs CSR_STATUS, t1
	fld f0,  TASK_SPOT_F0_F0(a0)
	fld f1,  TASK_SPOT_F1_F0(a0)
	fld f2,  TASK_SPOT_F2_F0(a0)
	fld f3,  TASK_SPOT_F3_F0(a0)
	fld f4,  TASK_SPOT_F4_F0(a0)
	fld f5,  TASK_SPOT_F5_F0(a0)
	fld f6,  TASK_SPOT_F6_F0(a0)
	fld f7,  TASK_SPOT_F7_F0(a0)
	fld f8,  TASK_SPOT_F8_F0(a0)
	fld f9,  TASK_SPOT_F9_F0(a0)
	fld f10, TASK_SPOT_F10_F0(a0)
	fld f11, TASK_SPOT_F11_F0(a0)
	fld f12, TASK_SPOT_F12_F0(a0)
	fld f13, TASK_SPOT_F13_F0(a0)
	fld f14, TASK_SPOT_F14_F0(a0)
	fld f15, TASK_SPOT_F15_F0(a0)
	fld f16, TASK_SPOT_F16_F0(a0)
	fld f17, TASK_SPOT_F17_F0(a0)
	fld f18, TASK_SPOT_F18_F0(a0)
	fld f19, TASK_SPOT_F19_F0(a0)
	fld f20, TASK_SPOT_F20_F0(a0)
	fld f21, TASK_SPOT_F21_F0(a0)
	fld f22, TASK_SPOT_F22_F0(a0)
	fld f23, TASK_SPOT_F23_F0(a0)
	fld f24, TASK_SPOT_F24_F0(a0)
	fld f25, TASK_SPOT_F25_F0(a0)
	fld f26, TASK_SPOT_F26_F0(a0)
	fld f27, TASK_SPOT_F27_F0(a0)
	fld f28, TASK_SPOT_F28_F0(a0)
	fld f29, TASK_SPOT_F29_F0(a0)
	fld f30, TASK_SPOT_F30_F0(a0)
	fld f31, TASK_SPOT_F31_F0(a0)
	fscsr t0
	csrc CSR_STATUS, t1
	ret
ENDPROC(__fstate_restore_asm)

	.section ".rodata"
	/* 异常向量表 */
ENTRY(excp_vect_table)
	RISCV_PTR handle_insn_misaligned_fault
	RISCV_PTR handle_insn_fault
	RISCV_PTR handle_insn_illegal_fault
	RISCV_PTR handle_break_fault
	RISCV_PTR handle_load_misaligned_fault
	RISCV_PTR handle_load_fault
	RISCV_PTR handle_store_misaligned_fault
	RISCV_PTR handle_store_fault
	RISCV_PTR handle_ecall_u_fault /* 系统调用 */
	RISCV_PTR handle_ecall_s_fault
	RISCV_PTR handle_unknown_fault
	RISCV_PTR handle_ecall_m_fault
	RISCV_PTR handle_mem_fault   /* 指令页面异常 */
	RISCV_PTR handle_mem_fault   /* 加载页面异常 */
	RISCV_PTR handle_unknown_fault
	RISCV_PTR handle_mem_fault   /* 储存页面异常 */
excp_vect_table_end:
END(excp_vect_table)
